#include "http_parser.h"

#define parse_single(s, x)			parse_element(s, x, 1)
#define check_parse_single(s, x)		if(parse_single(s, x)!=0) {verbose2("ERROR: bad check:'%s'\n",x[0]);} else {verbose2("good check:'%s'\n", x[0]);}

//identifica il tipo di header contenuto in 'cursor' tra tutti quelli specificati in 'elements' ('numbers' indica il numero massimo di elementi da controllare in 'elements')
int parse_element(char **cursor, char *elements[], int number) {
	//actually ignoring spaces
	while(**cursor==' ') {
		*cursor+=1;
	}
	int i;
	for(i=0; i<number; i++) {
		if(strncmp(elements[i], *cursor, strlen(elements[i])) == 0) {
			*cursor += strlen(elements[i]);
			verbose2("->type %d\n", i);
			return i;
		}
	}
	return -1;
}

//ritorna la lunghezza della riga di header contenuta nella stringa 's'
//ritorna un valore negativo in caso di errore
int parse_header_len(char *s) {
	char *e = strpbrk(s, http_CRLF[0]);
	if(e!=NULL)
		return e-s;
	else
		return -1;
}

//legge tutti gli header del pacchetto http contenuto nella stringa 'string' e li salva in 'headers'
void parse_headers(char **string, char headers[HTTP_MAX_HEADERS][HTTP_HEADER_LEN], char *elements[], int number) {
	int type, len;
	for(type=parse_element(string, elements, number); type!=-1; type=parse_element(string, elements, number)) {
		check_parse_single(string, http_header_delimiter);
		verbose3("adding:%d:%s\n", type, get_header(headers, type));
		len = parse_header_len(*string);
		set_header_len(headers, type, *string, len);
		*string+=len;
		check_parse_single(string, http_CRLF);
	}
}

//effettuano il parsing dell'head del pacchetto (rispettivamente di una request e una response) contenuto nella stringa 'string' e salvano le informazioni nella struttura 'r'
char* parse_request_head(char *string, http_req *r) {	
	char *s=string;
	r->method = parse_element(&s, http_rqst_method_string, http_rqst_method_len);
	r->uri = parse_element(&s, network_rqst_method_string, network_rqst_method_len);
	r->version = parse_element(&s, http_version, http_version_len);
	
	check_parse_single(&s, http_CRLF);
	
	parse_headers(&s, r->headers, http_rqst_header_string, http_rqst_header_len);
	check_parse_single(&s, http_CRLF);
	
	return s;
}

char* parse_response_head(char *string, http_resp *r) {
	char *s=string;
	r->version = parse_element(&s, http_version, http_version_len);
	r->code = parse_element(&s, http_rspn_code_string, http_rspn_code_len);
	r->reason = parse_element(&s, http_rspn_reason_string, http_rspn_reason_len);
	
	if(r->code!=r->reason)
		verbose3("response: code and reason not coherent (%d,%d)\n", r->code, r->reason);
	
	check_parse_single(&s, http_CRLF);
	
	parse_headers(&s, r->headers, http_rspn_header_string, http_rspn_header_len);
	check_parse_single(&s, http_CRLF);
	
	return s;
}

int _get_http_msg_type(char *s) {
	int i;
	for(i=0; i<http_version_len; i++) {
		if(strncmp(http_version[i], s, strlen(http_version[i])) == 0) {
			return HTTP_MSG_TYPE_RSPN;
		}
	}
	for(i=0; i<http_rqst_method_len; i++) {
		if(strncmp(http_rqst_method_string[i], s, strlen(http_rqst_method_string[i])) == 0) {
			return HTTP_MSG_TYPE_RQST;
		}
	}
	return -1;
}

//legge l'head di un pacchetto http dal socket specificato, salvando i dati in 'buffer'
char* read_http_head(int sock, char *buffer) {
	char *cursor; cursor=buffer;
	char d[20]; d[0]='\0'; strcat(d, http_CRLF[0]); strcat(d,d);
	int done=0, l; l = strlen(d);
	
	nread(sock, cursor, sizeof(char)*l);
	cursor+=sizeof(char)*l-sizeof(char);
	
	for(; strncmp(cursor-l+sizeof(char), d, l)!=0 ; cursor+=sizeof(char), nread(sock, cursor, sizeof(char))){
	}

	*(cursor+sizeof(char))='\0';
	return cursor;
}

//ricevono l'intero pacchetto http comprensivo di payload dal socket specificato 'sock' e ne salvano le informazioni nella struttura 'r'
void receive_http_req(int sock, http_req *r) {
	char buffer[PARSER_BUFFER_LEN];
	read_http_head(sock, buffer);
	parse_request_head(buffer, r);
	r->data_len = find_content_len(r->headers, HTTP_MSG_TYPE_RQST);
	read_http_data(sock, r->data, r->data_len);
	printf("received->");printf_request(r);
}

void receive_http_resp(int sock, http_resp *r) {
	char buffer[PARSER_BUFFER_LEN];
	read_http_head(sock, buffer);
	parse_response_head(buffer, r);
	r->data_len = find_content_len(r->headers, HTTP_MSG_TYPE_RSPN);
	read_http_data(sock, r->data, r->data_len);
	printf("received->");printf_response(r);
}

